package IC;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.OutputStreamWriter;

import java.io.IOException;
import java.util.List;

import java_cup.runtime.Symbol;

import IC.AST.*;
import IC.Parser.*;
import IC.SEMANTIC.SemanticAnalysisVisitor;
import IC.SEMANTIC.CreateTypeTableVisitor;
import IC.SEMANTIC.InitializationTestVisitor;
import IC.SEMANTIC.SemanticWarning;
import IC.lir.LIRTranslationVisitor;

public class Compiler {

	private static void errorOut() {
		System.out.println("Illigal arguments\nUsage: java IC.Compiler <file.ic> [-L<path to libic.sig>] [-print-ast]");
	}

	private static void syntaxErrorOut(List<SyntaxError> errors) {
		for (SyntaxError error : errors) {
			System.out.println(error);
		}
	}

	public static void main(String[] args) {
		if (args.length == 0) { // check arguments correctness
			errorOut();
			return;
		}
		String icFile = null, libFile = null;
		boolean boolAST = false;
		boolean printLir = false;
		boolean printSymbolTypeTables = false;
		final String lirOutFilePostfix = ".lir";

		//parse arguments
		for (int i = 0 ; i < args.length ; i++) {
			if (args[i].startsWith("-L"))
				libFile = args[i].substring(2);
			else if ("-print-ast".equalsIgnoreCase(args[i]))
				boolAST = true;
			else if ("-dump-symtab".equalsIgnoreCase(args[i]))
				printSymbolTypeTables = true;
			else if ("-print-lir".equalsIgnoreCase(args[i]))
				printLir = true;			
			else if (!args[i].startsWith("-"))
				icFile = args[i];
			else {
				errorOut();
				return;
			}
		}
		//check IC file correctness
		if (icFile == null) {
			System.out.println("IC file missing");
			errorOut();
			return;
		}

		FileReader icReader = null;
		try {
			icReader = new FileReader(icFile);
		} catch (IOException e) {
			System.out.println("Error openning IC file: "+icFile);
			return;
		}

		ICClass libClass = null;
		if (libFile != null) {
			FileReader libReader = null;
			try {
				libReader = new FileReader(libFile);
			} catch (IOException e) {
				System.out.println("Error openning library file: "+libFile);
				return;
			}

			//parse library file
			LibraryParser libParser = new LibraryParser(new Lexer(libReader));
			try {
				Symbol res = libParser.parse();
				if (libParser.GetSyntaxErrorsList().size() != 0) {
					syntaxErrorOut(libParser.GetSyntaxErrorsList());
					return;
				}
				libClass = (ICClass)res.value;
			} catch (Exception e) {
				System.out.println(e);
				return;
			}
		}

		//parse IC file
		Parser parser = new Parser(new Lexer(icReader));
		Program program = null; 
		try {
			Symbol res = parser.parse();
			if (parser.GetSyntaxErrorsList().size() > 0) {
				syntaxErrorOut(parser.GetSyntaxErrorsList());
				return;
			}
			program = (Program)res.value;	
		} catch (Exception e) {
			System.out.println(String.format("Error While parsing the file %s: %s", icFile ,e.getMessage()));
			e.printStackTrace();
			return;
		}
		
		if (libClass != null)
			program.ImportClass(libClass);
		
		
		//creating the type table
		CreateTypeTableVisitor typeTableVisitor = new CreateTypeTableVisitor(icFile);
		program.accept(typeTableVisitor);
		if (typeTableVisitor.getSemanticError()!=null) {
			System.out.println(typeTableVisitor.getSemanticError());
			return;
		}
		
		//print the symbol and type table if requested
		if (printSymbolTypeTables)
			System.out.println(typeTableVisitor.getTypeTable().toString());
		
        SemanticAnalysisVisitor semanticVisitor = new SemanticAnalysisVisitor(typeTableVisitor.getTypeTable(), icFile);
        program.accept(semanticVisitor);
        if (semanticVisitor.getError()!=null) {
            System.out.println(semanticVisitor.getError());
            return;
        }

        InitializationTestVisitor itv = new InitializationTestVisitor(typeTableVisitor.getTypeTable());
        program.accept(itv);
        for (SemanticWarning cw : itv.getSemanticWarningsList())
            System.out.println(cw);
        
        // get the lir translation output
        Object translatedLirOuput = program.accept(new LIRTranslationVisitor(semanticVisitor.getGlobalSymTable(), typeTableVisitor.getTypeTable()));
       
    	if (boolAST) {
    		PrettyPrinter progPP = new PrettyPrinter(icFile);
    		System.out.println(program.accept(progPP));
    	}
        
        if (printSymbolTypeTables)
            System.out.println(semanticVisitor.getGlobalSymTable().toString());
		
		//print if requested
		if (boolAST) {
			if (libClass != null) {
				PrettyPrinter libPP = new PrettyPrinter(libFile);
				System.out.println(libClass.accept(libPP));
			}
			PrettyPrinter programPP = new PrettyPrinter(icFile);
			System.out.println(program.accept(programPP));
		}
		
        if (printLir) {
            String outputLirPath = ((icFile.lastIndexOf(".")>=0) ? icFile.substring(0, icFile.lastIndexOf(".")) : icFile) + lirOutFilePostfix;
            FileWriter fileWriter = null;
            try {
                fileWriter = new FileWriter(outputLirPath);
                for (String instruction : (List<String>)translatedLirOuput) {
                    fileWriter.write(instruction+"\n");
                }
            } 
            catch (IOException e) {
                System.out.println("IO Error occured while writing to the output file \"" + outputLirPath + "\": " + e.getMessage());
            } 
            catch (Exception e){
            	System.out.println("Error: " + e.getMessage());
	        } 
            finally {
            	SafeCloseStream(fileWriter);
	       }


        }
	
	}

	private static void SafeCloseStream(OutputStreamWriter outputStreamWriter) {
		try{
			outputStreamWriter.close();
		}
		catch (Exception e){}
	}   
}